/**
 * Copyright (c) 2022 Presto Labs Pte. Ltd.
 * Author: Ju
 */

#include "coin2/base/tcp/tcp_connection.h"

#include <glog/logging.h>

namespace coin2::base::tcp {

TcpConnection::TcpConnection(boost::asio::io_context *ioc)
    : ioc_(ioc), strand_(*ioc), socket_(*ioc), buffer_(MAX_SIZE) {}

TcpConnection::~TcpConnection() { Close(); }

void TcpConnection::Close() {
  socket_.close();
  LOG(INFO) << "Tcp DisConnection";
}

void TcpConnection::Start() {
  auto self = shared_from_this();
  boost::asio::spawn(strand_, [this, self](boost::asio::yield_context yield) {
    boost::system::error_code ec;
    while (true) {
      std::size_t n = socket_.async_read_some(
          boost::asio::buffer(buffer_.data(), MAX_SIZE), yield[ec]);
      if (ec) {
        break;
      }
      LOG(INFO) << "read from socket packet size: " << n;
      callback_->OnRawDataReceived(
          self, memory::MemSlice(memory::MemBuf::Create(buffer_.data(), n)), n);
    }
    is_connect_ = false;
    callback_->OnError(self, ec);
  });
}

void TcpConnection::Connect(tcp::endpoint ep) {
  auto self = shared_from_this();
  socket_.async_connect(ep, [this, ep, self](boost::system::error_code ec) {
    if (ec) {
      LOG(ERROR) << " tcp connection connect error. please reconnect...";
      callback_->OnConnect(self, false);
      return;
    }
    is_connect_ = true;
    callback_->OnConnect(self, true);
  });
}
void TcpConnection::Connect(std::string ip, uint16_t port) {
  tcp::endpoint ep(boost::asio::ip::address::from_string(ip), port);
  Connect(std::move(ep));
}

void TcpConnection::SendMsg(std::string buffer) {
  auto self = shared_from_this();
  socket_.async_write_some(
      boost::asio::buffer(buffer.data(), buffer.size()),
      [this, self](boost::system::error_code ec, std::size_t n) {
        if (ec) {
          LOG(ERROR) << " tcp connection sendmsg error.";
          is_connect_ = false;
          callback_->OnError(self, ec);
        } else {
          callback_->OnDataSent(n);
        }
      });
}

void TcpConnection::SendMsg(memory::MemSlice buffer) {
  auto self = shared_from_this();
  memory::MemSlice data(buffer);
  socket_.async_write_some(
      boost::asio::buffer(data.Begin(), data.GetUsedSize()),
      [this, self](boost::system::error_code ec, std::size_t n) {
        if (ec) {
          LOG(ERROR) << " tcp connection sendmsg error.";
          is_connect_ = false;
          callback_->OnError(self, ec);
        } else {
          callback_->OnDataSent(n);
        }
      });
}

}  // namespace coin2::base::tcp